package com.gmrz.uap.common.db;

import com.gmrz.uaf.common.UAFRuntimeException;
import com.gmrz.util.Strings;
import org.apache.commons.codec.binary.Hex;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class DataSourceShardingConst {

    private static final Logger LOG = LogManager.getLogger(DataSourceShardingConst.class);

    public static final String DATASOURCE_IMPL_CLASS = "datasource.impl.class";
    public static final String SHARDING_KEY = "user_name";
    public static final String SHARDING_TABLE_AUTHENTICATORS = "t_authenticators";
    public static final String SHARDING_TABLE_DEVICES = "t_devices";
    public static final String SHARDING_TABLE_LIST = "t_authenticators,t_devices";
    public static final String SHARDING_LOGS_TABLE_LIST = "t_fido_log";
    public static final String SHARDING_TABLE_FIDO_LOG = "t_fido_log";

    //读写分离
    public static final String DATASOURCE_MASTERSLAVE_NAME = "datasource.masterslave.name";
    public static final String DATASOURCE_MASTERSLAVE_MASTER_JNDI = "datasource.masterslave.master.jndi";
    public static final String DATASOURCE_MASTERSLAVE_SLAVE_JNDI_LIST = "datasource.masterslave.slave.jndi.list";

    //仅分库
    public static final String DATASOURCE_SHARDING_DATABASE_JNDI_LIST = "datasource.sharding.database.jndi.list";
    public static final String DATASOURCE_SHARDING_DATABASE_JNDI_DEFAUT = "datasource.sharding.database.jndi.defaut";


    //仅分表
    public static final String DATASOURCE_SHARDING_TABLE_JNDI = "datasource.sharding.table.jndi";

    //分库分表
    public static final String DATASOURCE_SHARDING_DATABASEANDTABLE_JNDI_LIST = "datasource.sharding.databaseandtable.jndi.list";
    public static final String DATASOURCE_SHARDING_DATABASEANDTABLE_JNDI_DEFAULT = "datasource.sharding.databaseandtable.jndi.default";
    public static final String DATASOURCE_SHARDING_DATABASEANDTABLE_NAME = "datasource.sharding.databaseandtable.name";

    public static final String DATASOURCE_DATABASE_SHARDING_COUNT = "datasource.database.sharding.count";
    public static final String DATASOURCE_TABLE_SHARDING_COUNT = "datasource.table.sharding.count";
    public static final String DATASOURCE_JNDI_CORE_SINGLE = "datasource.jndi.core.single";

    //分库虚拟节点数量
    public static final String DATASOURCE_DATABASE_VISUAL_NODE_COUNT = "datasource.database.visual.node.count";

    //分库虚拟节点分配到物理节点的范围
    public static final String DATASOURCE_DATABASE_VISUAL_NODE_RANGE = "datasource.database.visual.node.range";

    //分表虚拟节点数量
    public static final String DATASOURCE_TABLE_VISUAL_NODE_COUNT = "datasource.table.visual.node.count";

    //分表虚拟节点分配到物理节点的范围
    public static final String DATASOURCE_TABLE_VISUAL_NODE_RANGE = "datasource.table.visual.node.range";

    //分库分表读写分离
    public static final String DATASOURCE_SHARDINGANDMASTERSLAVE_NAME = "datasource.shardingandmasterslave.name";
    public static final String DATASOURCE_SHARDINGANDMASTERSLAVE_DEFAULT_JNDI = "datasource.shardingandmasterslave.default.jndi";

    //日志数据源实现类
    public static final String DATASOURCE_LOG_IMPL_CLASS = "datasource.log.impl.class";
    public static final String DATASOURCE_JNDI_LOGS_SINGLE = "datasource.jndi.logs.single";

    public static final String DATASOURCE_SHARDING_LOGS_DATABASE_JNDI_LIST = "datasource.sharding.logs.database.jndi.list";
    public static final String DATASOURCE_LOGS_DATABASE_SHARDING_COUNT = "datasource.logs.database.sharding.count";

    public static final String DATASOURCE_SHARDING_LOGS_DATABASE_JNDI_DEFAULT = "datasource.sharding.logs.database.jndi.default";
    public static final String _DEFAULT_SEPARATOR_COMMA = ",";
    private static Properties prop = new Properties();

    static {
        try {
            prop.load(DataSourceShardingConst.class.getResourceAsStream("/sharding-db.properties"));
        } catch (Exception e) {
            LOG.error("DataSourceShardingConst static prop error",e);
        }
    }


    public static String[] getJndiSharding(String key) {
        return prop.getProperty(key).trim().split(_DEFAULT_SEPARATOR_COMMA);
    }

    public static String getJndiSingle(String key) {
        return getJndiSharding(key)[0];
    }


    public static DataSource getDataSourceForJndi(String jndiName) {
        DataSource dataSource = null;

        try {
            dataSource = (DataSource) new InitialContext().lookup("java:comp/env/" + jndiName);
        } catch (NamingException e) {
            LOG.error("获取不到" + jndiName + "的JNDI名称", e);
        }
        return dataSource;
    }

    public static List<String> getJndiListFromString(String jndi) {
        List<String> jndiList = new ArrayList<String>();
        if (Strings.isNullOrEmpty(jndi)) {
            return jndiList;
        }

        String[] jndiArr = jndi.split(_DEFAULT_SEPARATOR_COMMA);
        for (String jndiTmp : jndiArr) {
            jndiList.add(jndiTmp);
        }

        return jndiList;
    }


    public static String getConfigInfo(String key) {
        String value = prop.getProperty(key).trim();
        LOG.info("value from sharding-db.properties ==> " + value);
        return value;
    }

    public static Integer getConfigInfoInt(String key) {
        Integer value = Integer.parseInt(prop.getProperty(key));
        LOG.info("value from sharding-db.properties ==> " + value);
        return value;
    }


    public static String getDatabaseTableShardingRule(String dbName, String tableName) {
        StringBuilder rule = new StringBuilder();
        Integer dbShardingCount = getConfigInfoInt(DATASOURCE_DATABASE_SHARDING_COUNT);
        Integer tableShardingCount = getConfigInfoInt(DATASOURCE_TABLE_SHARDING_COUNT);

        rule.append(dbName);
        rule.append("${0..");
        rule.append(dbShardingCount - 1);
        rule.append("}.");
        rule.append(tableName);
        rule.append("${[");

        for (int i = 0; i < tableShardingCount; i++) {
            rule.append(i);
            if (i != tableShardingCount - 1) {
                rule.append(",");
            }
        }

        rule.append("]}");

        return rule.toString();
    }


    public static String getDatabaseSingleTableShardingRule(String dbName, String tableName) {
        StringBuilder rule = new StringBuilder();
        Integer tableShardingCount = getConfigInfoInt(DATASOURCE_TABLE_SHARDING_COUNT);

        rule.append(dbName);
        rule.append(".");
        rule.append(tableName);
        rule.append("${[");

        for (int i = 0; i < tableShardingCount; i++) {
            rule.append(i);
            if (i != tableShardingCount - 1) {
                rule.append(",");
            }
        }

        rule.append("]}");

        return rule.toString();
    }


    /**
     * 根据取模后的值获取虚拟节点对应的物理节点
     *
     * @param mod 取模数据
     * @return 物理节点
     */
    public static Integer getPhysicNodeByVisualNode(int mod, String rangeList) {
        String[] rangArr = rangeList.split(_DEFAULT_SEPARATOR_COMMA);
        Integer physicCount = null;
        for (int i = 0; i < rangArr.length; i++) {
            String[] tmpArr = rangArr[i].split("-");
            if ((mod >= Integer.parseInt(tmpArr[0])) && (mod <= Integer.parseInt(tmpArr[1]))) {
                physicCount = i;
                break;
            }
        }

        return physicCount;
    }

    public static String generateHashedUserName(String userName) throws UAFRuntimeException {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("sha-1");
            byte[] hash = messageDigest.digest(userName.getBytes());
            LOG.info("Generate user identity={}", Hex.encodeHexString(hash));
            return Hex.encodeHexString(hash);
        } catch (Exception e) {
            LOG.error("{} {}", "Generate user identity failed", e);
            throw new UAFRuntimeException("Generate user identity failed", e);
        }
    }


    public static void main(String[] args) {
        int[] dd = new int[]{0, 12, 90, 159};
        String rangeList = DataSourceShardingConst.getConfigInfo(DataSourceShardingConst.DATASOURCE_TABLE_VISUAL_NODE_RANGE);
        for (int tt : dd) {
            getPhysicNodeByVisualNode(tt, rangeList);
        }
    }

}
